package com.harmony.pilot.completion.service.impl;

import com.harmony.pilot.common.dto.Diagnostic;
import com.harmony.pilot.completion.client.CompilationDatabaseClient;
import com.harmony.pilot.completion.service.DiagnosticService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 诊断服务实现
 * 使用编译数据库进行实时错误检查
 */
@Service
@Slf4j
public class DiagnosticServiceImpl implements DiagnosticService {
    
    private final CompilationDatabaseClient compilationDatabaseClient;
    
    @Value("${diagnostic.clang-path:clang++}")
    private String clangPath;
    
    @Value("${diagnostic.temp-dir:./temp}")
    private String tempDir;
    
    public DiagnosticServiceImpl(CompilationDatabaseClient compilationDatabaseClient) {
        this.compilationDatabaseClient = compilationDatabaseClient;
    }
    
    @Override
    public List<Diagnostic> checkFile(String filePath, String code, String projectRoot) {
        log.debug("Checking file: {}", filePath);
        
        List<Diagnostic> diagnostics = new ArrayList<>();
        
        // 1. 语法检查
        diagnostics.addAll(checkSyntax(filePath, code, projectRoot));
        
        // 2. 类型检查
        diagnostics.addAll(checkTypes(filePath, code, projectRoot));
        
        // 3. 编译检查
        diagnostics.addAll(checkCompilation(filePath, code, projectRoot));
        
        return diagnostics;
    }
    
    @Override
    @Cacheable(value = "syntax-checks", key = "#filePath + ':' + #code.hashCode()")
    public List<Diagnostic> checkSyntax(String filePath, String code, String projectRoot) {
        List<Diagnostic> diagnostics = new ArrayList<>();
        
        // 基础语法检查（括号匹配、分号等）
        try {
            // 检查括号匹配
            int openBraces = countOccurrences(code, "{");
            int closeBraces = countOccurrences(code, "}");
            if (openBraces != closeBraces) {
                Diagnostic diagnostic = new Diagnostic();
                diagnostic.setSeverity("error");
                diagnostic.setMessage("Unmatched braces: " + openBraces + " opening, " + closeBraces + " closing");
                diagnostic.setFilePath(filePath);
                diagnostic.setLine(1);
                diagnostic.setColumn(1);
                diagnostics.add(diagnostic);
            }
            
            // 检查括号匹配
            int openParens = countOccurrences(code, "(");
            int closeParens = countOccurrences(code, ")");
            if (openParens != closeParens) {
                Diagnostic diagnostic = new Diagnostic();
                diagnostic.setSeverity("error");
                diagnostic.setMessage("Unmatched parentheses: " + openParens + " opening, " + closeParens + " closing");
                diagnostic.setFilePath(filePath);
                diagnostic.setLine(1);
                diagnostic.setColumn(1);
                diagnostics.add(diagnostic);
            }
            
        } catch (Exception e) {
            log.debug("Failed to check syntax", e);
        }
        
        return diagnostics;
    }
    
    @Override
    @Cacheable(value = "type-checks", key = "#filePath + ':' + #code.hashCode()")
    public List<Diagnostic> checkTypes(String filePath, String code, String projectRoot) {
        List<Diagnostic> diagnostics = new ArrayList<>();
        
        // 使用编译数据库获取编译命令，然后进行类型检查
        try {
            CompilationDatabaseClient.CompilationCommand cmd = 
                compilationDatabaseClient.queryCompilationCommand(filePath, projectRoot);
            
            if (cmd != null) {
                // 使用clang进行类型检查
                diagnostics.addAll(checkTypesWithClang(filePath, code, cmd));
            }
            
        } catch (Exception e) {
            log.debug("Failed to check types", e);
        }
        
        return diagnostics;
    }
    
    @Override
    @Cacheable(value = "compilation-checks", key = "#filePath + ':' + #code.hashCode()")
    public List<Diagnostic> checkCompilation(String filePath, String code, String projectRoot) {
        List<Diagnostic> diagnostics = new ArrayList<>();
        
        try {
            CompilationDatabaseClient.CompilationCommand cmd = 
                compilationDatabaseClient.queryCompilationCommand(filePath, projectRoot);
            
            if (cmd != null) {
                // 使用编译命令进行编译检查
                diagnostics.addAll(checkCompilationWithCommand(filePath, code, cmd, projectRoot));
            }
            
        } catch (Exception e) {
            log.debug("Failed to check compilation", e);
        }
        
        return diagnostics;
    }
    
    /**
     * 使用clang进行类型检查
     */
    private List<Diagnostic> checkTypesWithClang(
            String filePath, 
            String code, 
            CompilationDatabaseClient.CompilationCommand cmd) {
        
        List<Diagnostic> diagnostics = new ArrayList<>();
        
        try {
            // 创建临时文件
            Path tempFile = Paths.get(tempDir, "check_" + System.currentTimeMillis() + ".cpp");
            Files.createDirectories(tempFile.getParent());
            Files.writeString(tempFile, code);
            
            // 构建编译命令（只检查，不生成目标文件）
            String command = cmd.getCommand()
                    .replace("-c", "-fsyntax-only")
                    .replace(filePath, tempFile.toString());
            
            // 执行命令
            ProcessBuilder pb = new ProcessBuilder("sh", "-c", command);
            pb.directory(new File(cmd.getDirectory()));
            pb.redirectErrorStream(true);
            
            Process process = pb.start();
            
            // 解析错误输出
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getErrorStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    Diagnostic diagnostic = parseClangError(line, filePath);
                    if (diagnostic != null) {
                        diagnostics.add(diagnostic);
                    }
                }
            }
            
            process.waitFor();
            
            // 清理临时文件
            Files.deleteIfExists(tempFile);
            
        } catch (Exception e) {
            log.debug("Failed to check types with clang", e);
        }
        
        return diagnostics;
    }
    
    /**
     * 使用编译命令进行编译检查
     */
    private List<Diagnostic> checkCompilationWithCommand(
            String filePath,
            String code,
            CompilationDatabaseClient.CompilationCommand cmd,
            String projectRoot) {
        
        List<Diagnostic> diagnostics = new ArrayList<>();
        
        try {
            // 创建临时文件
            Path tempFile = Paths.get(tempDir, "compile_" + System.currentTimeMillis() + ".cpp");
            Files.createDirectories(tempFile.getParent());
            Files.writeString(tempFile, code);
            
            // 构建编译命令
            String command = cmd.getCommand().replace(filePath, tempFile.toString());
            
            // 执行命令
            ProcessBuilder pb = new ProcessBuilder("sh", "-c", command);
            pb.directory(new File(cmd.getDirectory()));
            pb.redirectErrorStream(true);
            
            Process process = pb.start();
            
            // 解析错误输出
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getErrorStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    Diagnostic diagnostic = parseCompilationError(line, filePath);
                    if (diagnostic != null) {
                        diagnostics.add(diagnostic);
                    }
                }
            }
            
            process.waitFor();
            
            // 清理临时文件
            Files.deleteIfExists(tempFile);
            
        } catch (Exception e) {
            log.debug("Failed to check compilation", e);
        }
        
        return diagnostics;
    }
    
    /**
     * 解析clang错误
     */
    private Diagnostic parseClangError(String line, String filePath) {
        // 错误格式: file:line:column: error: message
        Pattern pattern = Pattern.compile("(.+):(\\d+):(\\d+):\\s+(error|warning|note):\\s+(.+)");
        Matcher matcher = pattern.matcher(line);
        
        if (matcher.find()) {
            Diagnostic diagnostic = new Diagnostic();
            diagnostic.setFilePath(filePath);
            diagnostic.setLine(Integer.parseInt(matcher.group(2)));
            diagnostic.setColumn(Integer.parseInt(matcher.group(3)));
            diagnostic.setSeverity(matcher.group(4));
            diagnostic.setMessage(matcher.group(5));
            return diagnostic;
        }
        
        return null;
    }
    
    /**
     * 解析编译错误
     */
    private Diagnostic parseCompilationError(String line, String filePath) {
        // 与clang错误格式相同
        return parseClangError(line, filePath);
    }
    
    /**
     * 统计字符串出现次数
     */
    private int countOccurrences(String text, String pattern) {
        int count = 0;
        int index = 0;
        while ((index = text.indexOf(pattern, index)) != -1) {
            count++;
            index += pattern.length();
        }
        return count;
    }
}

